using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.Reflection;
using System.Security.Permissions;
using System.ComponentModel;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Data;
using System.Data.Odbc;
using NumericalRecipes;
using FftLib;
using BmgNet;
using StaticUtility;
using EngMATLib;

namespace ObjectLib
{
	/// <summary>
	/// Represents the portion of a JunctionF object concerned with only one owner at a time.
	/// </summary>
	public class SubJunctionF
	{
		public JunctionF Master;
		public PathF Owner;
		public VertexF[] Targets;
		public double Orientation;
		public double Sin;
		public double Cos;
		public SubJunctionF(JunctionF Master, PathF Owner, VertexF[] Targets)
		{
			this.Master = Master;
			this.Owner = Owner;
			this.Targets = Targets;
			if( Targets==null )
				Orientation =  Sin = Cos = double.NaN;
			else if(Targets.Length==1)
			{
				Orientation = Math.Atan2(Targets[0].Y-Master.Center.Y,Targets[0].X-Master.Center.X);
				Cos = Math.Cos(Orientation);
				Sin = Math.Sin(Orientation);
			}
			else if(Targets.Length==2)
			{
				Orientation = Math.Atan2(Targets[1].Y-Targets[0].Y,Targets[1].X-Targets[0].X);
				Cos = Math.Cos(Orientation);
				Sin = Math.Sin(Orientation);
			}
		}
	}
	/// <summary>
	/// This class represents instances of T-junctions.  A T-junction is an intersection of two paths, Primary and Secondary.
	/// A T-junction can ONLY occur at the end of a ContourF or Edge.
	/// </summary>
	public class JunctionF
	{
		/// <summary>
		/// The junction's central point.
		/// </summary>
		public VertexF Center;
		/// <summary>
		/// The The VertexF objects to which the junction can branch to (from the Center).
		/// </summary>
		public VertexF[] Targets;
		/// <summary>
		/// The PathF owners of this JunctionF.
		/// </summary>
		public PolyFarray Polygons
		{
			get
			{
				if( Center==null )
					return null;
				return Center.Polygons;
			}
		}
		/// <summary>
		/// Gives the targets associated with each member of Owners.  There can be 0, 1, or 2 targets for each owner.
		/// Thus, this has a dimension [# of owners] [# of targets for that owner].  When an owner has 0 targets,
		/// the entry is 'null' (instead of VertexF[0]).
		/// </summary>
		public SubJunctionF[] ByOwners;
		/// <summary>
		/// The orientation of each Target from the Center.
		/// Automatically calculated in constructor.
		/// </summary>
		public double[] Orientation;
		/// <summary>
		/// The cosine of each Orientation (cached for drawing purposes).
		/// Automatically calculated in constructor.
		/// </summary>
		public double[] Cos;
		/// <summary>
		/// The sine of each Orientation (cached for drawing purposes).
		/// Automatically calculated in constructor.
		/// </summary>
		public double[] Sin;
		/// <summary>
		/// The path distance to the next adjacent junction (should be positive only).
		/// The user updates this value, it is not internally updated by this class.
		/// The default value is float.NaN.
		/// </summary>
		public float Clearance = float.NaN;
		/// <summary>
		/// Send a structure representation of this instance to matlab variable 'varName'.
		/// </summary>
		/// <param name="varName">Contains everything appended before the fieldnames of the structure.  For example, "struct_name(1)."</param>
		/// <param name="mat">An object that provides access to matlab.</param>		
		public void ToMatlabStruct(string varName, EngMATAccess mat)
		{
		}
		/// <summary>
		/// Constructs a junction from the VertexF at the center.
		/// </summary>
		/// <param name="Center"></param>
		public JunctionF(VertexF Center)
		{
			this.Center = Center;
			this.ByOwners = new SubJunctionF[Center.Polygons.Count];
			_tar.Clear();

			//	Compute the possible targets
			int i, ind, next, last, ct;
			PathF path;
			for(i=0; i<Center.Polygons.Count; i++)
			{
				path = Center.Polygons[i];
				ind = path.IndexOf(Center);
				next = ind+1;
				last = ind-1;
				//if( path.GetType()==typeof(PolygonF) )
				//{
					if( next >= path.Count )
						next = 0;
					if( last < 0 )
						last = path.Count-1;
				//}
				ct = 0;
				if( next<path.Count )
				{
					_tar.Add(path[next]);
					ct++;
				}
				if( last>=0 )
				{
					_tar.Add(path[last]);
					ct++;
				}
				if(ct>2)
					throw new ApplicationException("duh");
				else if(ct==2)
					ByOwners[i] = new SubJunctionF(this,Center.Polygons[i], new VertexF[] {path[next], path[last]});
				else if(next<path.Count)
                    ByOwners[i] = new SubJunctionF(this,Center.Polygons[i],new VertexF[] { path[next] });
				else if(last>=0)
                    ByOwners[i] = new SubJunctionF(this,Center.Polygons[i],new VertexF[] { path[last] });
			}
			if(_tar.Count > 0)
			{
				Targets = (VertexF[])Util.Unique(_tar).ToArray(typeof(VertexF));
				Orientation = new double[Targets.Length];
				Cos = new double[Targets.Length];
				Sin = new double[Targets.Length];
				for(i=0; i<Targets.Length; i++)
				{
					Orientation[i] = Math.Atan2(Targets[i].Y-Center.Y,Targets[i].X-Center.X);
					Cos[i] = Math.Cos(Orientation[i]);
					Sin[i] = Math.Sin(Orientation[i]);
				}
			}
		}
		private static ArrayList _tar;
		static JunctionF()
		{
			_tar = new ArrayList();
		}
		/// <summary>
		/// Constructs an array of JunctionF objects associated with obj, which can be either
		/// a CategoryNode, ParsedObject, PolygonF, or ContourF.
		/// </summary>
		/// <param name="obj">A CategoryNode, ParsedObject, PolygonF, or ContourF.</param>
		/// <param name="doRecursion">When true, this is executed on all subordinate categories.
		/// This only has affects the case when obj is a CategoryNode with sub-categories.</param>
		/// <returns>The junctions (excluding x-junctions) associated with 'obj'.</returns>
		public static JunctionF[] ConstructJunctions(object obj, bool doRecursion)
		{
			VertexF[] juns = EnumerateUniqueJunctions(obj, doRecursion);
			if( juns==null || juns.Length==0 )
				return null;
			JunctionF[] output = new JunctionF[juns.Length];
			for(int i=0; i<juns.Length; i++)
				output[i] = new JunctionF(juns[i]);
			return output;
		}
		public static VertexF[] EnumerateUniqueJunctions(object obj, bool doRecursion)
		{
			ArrayList list = new ArrayList();
			EnumerateJunctions(obj, doRecursion, list);
			if( list.Count<=0 )
				return null;
			list = Util.Unique(list);
			return (VertexF[])list.ToArray( typeof(VertexF) );
		}
		public static void EnumerateJunctions(object obj, bool doRecursion, ArrayList output)
		{
			if(obj!=null)
			{
				int i;
				if( obj.GetType()==typeof(CategoryNode) )
				{
					CategoryNode cn = (CategoryNode)obj;
					for(i=0; i<cn.Objects.Count; i++)
						EnumerateJunctions(cn.Objects[i], doRecursion, output);
					if( doRecursion )
					{
						for(i=0; i<cn.Sub.Count; i++)
							EnumerateJunctions(cn.Sub[i], true, output);
					}
				}
				else if(obj.GetType()==typeof(ParsedObject))
				{
					ParsedObject po = (ParsedObject)obj;
					for(i=0; i<po.Polygons.Count; i++)
						EnumerateJunctions(po.Polygons[i], doRecursion, output);
				}
				else if(obj.GetType()==typeof(PolygonF))
				{
					PolygonF poly = (PolygonF)obj;
					ArrayList list = poly.EnumerateJunctions();
					for(i=0; i<list.Count; i++)
						output.Add( poly[(int)list[i]] );
                    /*
					if(obj.GetType()==typeof(PolygonF))
					{
						int j;
						PolygonF poly = (PolygonF)obj;
						for(i=0; i<poly.Contours.Count; i++)
						{
							path = poly.Contours[i];
							list = path.EnumerateJunctions();
							for(j=0; j<list.Count; j++)
								output.Add( path[(int)list[j]] );
						}
					}
                    */
				}
                else if(obj.GetType()==typeof(ContourF))
                {
                    //  This shouldn't happen.
                }
			}
		}
	}
	/// <summary>
	/// This class represents instances of X-junctions.  An X-junction is an intersection of two paths, Pf1 and Pf2.
	/// </summary>
	public class Xjunction
	{
		/// <summary>
		/// The location of the X-junction.
		/// </summary>
		public PointF Location;
		/// <summary>
		/// One of two paths involved in the x-junction.
		/// </summary>
		public PathF Pf1;
		/// <summary>
		/// One of two paths involved in the x-junction.
		/// </summary>
		public PathF Pf2;
		/// <summary>
		/// The index along Pf1 where this X-junction exists.  Values between integers indicate that
		/// the intersection lies between two indices.
		/// </summary>
		public float Ind1;
		/// <summary>
		/// The index along Pf2 where this X-junction exists.  Values between integers indicate that
		/// the intersection lies between two indices.
		/// </summary>
		public float Ind2;
		/// <summary>
		/// The orientation of path Pf1 at Location in radians.
		/// </summary>
		public double Orientation1;
		/// <summary>
		/// The orientation of path Pf2 at Location in radians.
		/// </summary>
		public double Orientation2;
		/// <summary>
		/// The cosine and sine of Orientation1 and Orientation2.
		/// </summary>
		public double Cos1, Cos2, Sin1, Sin2;
		/// <summary>
		/// The path distance to the next adjacent junction (should be positive only).
		/// The user updates this value, it is not internally updated by this class.
		/// The default value is float.NaN.
		/// </summary>
		public float Clearance = float.NaN;
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="Pf1"></param>
		/// <param name="Pf2"></param>
		/// <param name="Ind1"></param>
		/// <param name="Ind2"></param>
		public Xjunction(PathF Pf1, PathF Pf2, float Ind1, float Ind2, PointF Location)
		{
			this.Pf1 = Pf1;
			this.Pf2 = Pf2;
			this.Ind1 = Ind1;
			this.Ind2 = Ind2;
			this.Location = Location;
			Orientation1 = Pf1.OrientationAt(Ind1);
			Orientation2 = Pf2.OrientationAt(Ind2);
			Cos1 = Math.Cos(Orientation1);
			Sin1 = Math.Sin(Orientation1);
			Cos2 = Math.Cos(Orientation2);
			Sin2 = Math.Sin(Orientation2);
		}
		/// <summary>
		/// Send a structure representation of this instance to matlab variable 'varName'.
		/// </summary>
		/// <param name="varName">Contains everything appended before the fieldnames of the structure.  For example, "struct_name(1)."</param>
		/// <param name="mat">An object that provides access to matlab.</param>		
		public void ToMatlabStruct(string varName, EngMATAccess mat)
		{
		}
		/// <summary>
		/// Finds the X-junction for two PathF objects (if any X-junctions that exist).
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns>An array of Xjunction objects or null if there are no X-junctions.</returns>
		public static Xjunction[] Junctions(PathF a, PathF b)
		{
			float[] aInd, bInd;
			PointF[] pf = PathF.IntersectsX(a,b, out aInd, out bInd);
			if(pf==null || pf.Length==0)
				return null;
			Xjunction[] output = new Xjunction[pf.Length];
			for(int i=0; i<pf.Length; i++)
				output[i] = new Xjunction(a,b,aInd[i],bInd[i],pf[i]);
			return output;
		}
	}
}